//file name: ipsend.v
//author:    shugenyin
//date:		 2018.1.2
//function:  IP Sender
//log:

module ipsend(
	input              clk,                   //GMII发送的时钟信号
	input              rst_n,
	output reg         txen,                  //GMII数据使能信号
	output reg         txer,                  //GMII发送错误信号
	output reg [7:0]   dataout,               //GMII发送数据

	input      [31:0]  crc,                   //CRC32校验码
	output reg         crcen,                 //CRC32 校验使能
	output reg         crcre,                 //CRC32 校验清除
				  
	input      [7:0]   datain,                //数据输入	
	input              key_valid, 

	output reg         fifo_rd_en,            //FIFO读使能 				  
	input      [10:0]  frame_index,           //IP包的序号

	output reg [3:0]   tx_state,              //发送状态机    
	input      [15:0]  tx_data_length,        //发送的数据包的长度
	input      [15:0]  tx_total_length        //发送包的长度
);


reg [31:0] ip_header [6:0];                  //数据段为1K

reg [7:0] preamble [7:0];                    //preamble
reg [7:0] mac_addr [13:0];                   //mac address
reg [4:0] i,j;

reg [31:0] check_buffer;
reg [15:0] tx_data_counter;

parameter IDLE    		= 4'b0000;
parameter START   		= 4'b0001;
parameter MAKE    		= 4'b0010;
parameter SEND55  		= 4'b0011;
parameter SENDMAC 		= 4'b0100;
parameter SEND_HEADER	= 4'b0101;
parameter SEND_DATA		= 4'b0110;
parameter SEND_CRC		= 4'b0111;

initial
  begin
	 tx_state<=IDLE;
	 //定义IP 包头
	 preamble[0]  <= 8'h55;                 //7个前导码55,一个帧开始符d5
	 preamble[1]  <= 8'h55;
	 preamble[2]  <= 8'h55;
	 preamble[3]  <= 8'h55;
	 preamble[4]  <= 8'h55;
	 preamble[5]  <= 8'h55;
	 preamble[6]  <= 8'h55;
	 preamble[7]  <= 8'hD5;
	 mac_addr[0]  <= 8'hFF;                 //目的MAC地址 ff-ff-ff-ff-ff-ff, 全ff为广播包
	 mac_addr[1]  <= 8'hFF;
	 mac_addr[2]  <= 8'hFF;
	 mac_addr[3]  <= 8'hFF;
	 mac_addr[4]  <= 8'hFF;
	 mac_addr[5]  <= 8'hFF;
	 mac_addr[6]  <= 8'h03;                 //默认的开发板源MAC地址 03-08-35-01-AE-C2, 用户也可以修改
	 mac_addr[7]  <= 8'h08;
	 mac_addr[8]  <= 8'h35;
	 mac_addr[9]  <= 8'h01;
	 mac_addr[10] <= 8'hAE;
	 mac_addr[11] <= 8'hC2;
	 mac_addr[12] <= 8'h08;                //0800: IP包类型
	 mac_addr[13] <= 8'h00;
	 i <= 0;
 end


//UDP数据发送程序	 
always @(posedge clk)
begin		
	if(~rst_n)
		begin
			txer <= 1'b0;
			txen <= 1'b0;
			crcen <= 1'b0;
 		    fifo_rd_en <= 1'b0;  				 
			crcre <= 1;
			j <= 0;
			dataout <= 0;
			tx_data_counter <= 0;	
			tx_state <= IDLE;
		end
	else
		case(tx_state)
			IDLE:
				begin
					txer <= 1'b0;
					txen <= 1'b0;
					crcen <= 1'b0;
   		         	fifo_rd_en <= 1'b0;  				 
					crcre <= 1;
					j <= 0;
					dataout <= 0;
					tx_data_counter <= 0;
   		          	if(key_valid)     //如果FIFO中的数据大于1200
					    tx_state <= START;  
   		         	else
					    tx_state <= IDLE;  			
				end
			START:
				begin        
					//IP header
					ip_header[0] <= {16'h4500,tx_total_length};        //版本号：4； 包头长度：20；IP包总长
				 	ip_header[1][31:16] <= {5'b00000,frame_index};     //包序列号
					ip_header[1][15:0] <= 16'h4000;                    //Fragment offset
					ip_header[2] <= 32'h80110000;                      //mema[2][15:0] 协议：17(UDP)
					ip_header[3] <= 32'hc0_a8_0a_7a;                   //192.168.10.122源地址
					ip_header[4] <= 32'hc0_a8_0a_43;                   //192.168.10.67目的地址广播地址
					ip_header[5] <= 32'h8000_8000;                     //2个字节的源端口号和2个字节的目的端口号
					ip_header[6] <= {tx_data_length,16'h0000};         //2个字节的数据长度和2个字节的校验和（无）
	   				tx_state<=MAKE;
        		end	
        	MAKE:
				begin            //生成包头的校验和
				    if(i==0) 
						begin
							check_buffer <= ((ip_header[0][15:0]+ip_header[0][31:16])+(ip_header[1][15:0]+ip_header[1][31:16]))+(((ip_header[2][15:0]+
						               ip_header[2][31:16])+((ip_header[3][15:0]+ip_header[3][31:16])))+(ip_header[4][15:0]+ip_header[4][31:16]));
    		            	i <= i+1'b1;
						end
    		        else if(i==1) 
						begin
							check_buffer[15:0] <= check_buffer[31:16]+check_buffer[15:0];
							i <= i+1'b1;
				 		end
			  		else
						begin
				    		ip_header[2][15:0] <= ~check_buffer[15:0];                 //header checksum
							i <= 0;
							tx_state <= SEND55;
						end
				end
			SEND55: 
				begin                               //发送8个IP前导码:7个55, 1个d5                    
 					txen <= 1'b1;                             //GMII数据发送有效
					crcre <= 1'b1;                            //reset crc  
					if(i==7) 
						begin
            				dataout[7:0] <= preamble[i][7:0];
							i <= 0;
							tx_state <= SENDMAC;
						end
					else 
						begin                        
					    	dataout[7:0] <= preamble[i][7:0];
					    	i <= i+1;
						end
				end	
			SENDMAC: 
				begin                           //发送目标MAC address和源MAC address和IP包类型  
			 		crcen <= 1'b1;                            //crc校验使能，crc32数据校验从目标MAC开始		
					crcre <= 1'b0;                            			
            		if(i==13) 
						begin
            				dataout[7:0] <= mac_addr[i][7:0];
							i <= 0;
				   			tx_state <= SEND_HEADER;
						end
					else 
						begin                        
						    dataout[7:0] <= mac_addr[i][7:0];
						    i <= i + 1'b1;
						end
				end
			SEND_HEADER: 
				begin                        //发送7个32bit的IP 包头
					if(j==6) 
						begin                        //发送ip_header[6]                   
							if(i==0) 
					 			begin
									dataout[7:0] <= ip_header[j][31:24];
									i <= i + 1'b1;
								end
							else if(i==1) 
								begin
									dataout[7:0] <= ip_header[j][23:16];
									i <= i + 1'b1;
					 			end
							else if(i==2) 
					  			begin
									dataout[7:0] <= ip_header[j][15:8];
									i <= i + 1'b1;
								end
							else if(i==3) 
								begin
									dataout[7:0] <= ip_header[j][7:0];
									i <= 0;
									j <= 0;
									tx_state <= SEND_DATA;
                					fifo_rd_en <= 1'b1;                    //读FIFO数据有效						 
								end
							else
								txer <= 1'b1;
						end		 
					else
						begin                                   //发送ip_header[0]~ip_header[5]   
							if(i==0) 
					 			begin
									dataout[7:0] <= ip_header[j][31:24];
									i <= i + 1'b1;
								end
							else if(i==1) 
								begin
									dataout[7:0] <= ip_header[j][23:16];
									i <= i + 1'b1;
								end
							else if(i==2) 
								begin
									dataout[7:0] <= ip_header[j][15:8];
									i <= i + 1'b1;
								end
							else if(i==3) 
								begin
									dataout[7:0] <= ip_header[j][7:0];
									i <= 0;
									j <= j + 1'b1;
								end					
							else
								txer <= 1'b1;
						end
				end
			SEND_DATA:
				begin                                      //发送UDP数据包
					if(tx_data_counter==tx_data_length-9) 
						begin       //判断是否是发送最后的数据(真正的数据包长度是tx_data_length-8）
							tx_state <= SEND_CRC;	                          //发送最后一个字节,状态转到sendcrc
 		        			dataout <= datain;
							fifo_rd_en <= 1'b0;
            			end
            		else 
						begin                                       //发送其它的数据包(第一个字节到倒数第二个字节）
            				tx_data_counter <= tx_data_counter + 1'b1;			
							tx_state <= tx_state;	                         //发送最后一个字节,状态转到sendcrc
 		        			dataout <= datain;
							fifo_rd_en <= 1'b1;
						end
				end	
			SEND_CRC: 
				begin                              //发送32位的crc校验
					fifo_rd_en <= 1'b0;
					crcen<=1'b0;
					if(i==0)	
						begin
							dataout[7:0] <= {~crc[24], ~crc[25], ~crc[26], ~crc[27], ~crc[28], ~crc[29], ~crc[30], ~crc[31]};
							i <= i + 1'b1;
						end
					else 
						begin
							if(i==1) 
								begin
									dataout[7:0] <= {~crc[16], ~crc[17], ~crc[18], ~crc[19], ~crc[20], ~crc[21], ~crc[22], ~crc[23]};
									i <= i + 1'b1;
								end
							else if(i==2) 
								begin
									dataout[7:0] <= {~crc[8], ~crc[9], ~crc[10], ~crc[11], ~crc[12], ~crc[13], ~crc[14], ~crc[15]};
									i <= i + 1'b1;
								end
							else if(i==3) 
								begin
									dataout[7:0] <= {~crc[0], ~crc[1], ~crc[2], ~crc[3], ~crc[4], ~crc[5], ~crc[6], ~crc[7]};
									i <= 0;
									tx_state <= IDLE;
								end
							else 
								begin
                					txer <= 1'b1;
				 				end
						end
				end					
			default:tx_state<=IDLE;		
    endcase	  
end
 
 
endmodule

